import numpy as np
from sklearn.model_selection import train_test_split
from collections import Counter

"""
需求：手撕knn，使用测试集评估，并实现随机输入一个点能判断是哪一类
"""

def readFile(filename):
    x = []
    y = []
    with open(filename, "rb") as fp:
        while True:
            # 40920	8.326976	0.953952	3
            line = fp.readline().decode().strip()  # 去除前后空格
            if line:
                arr = line.split("\t")
                y.append(eval(arr[3]))
                for i in range(3):
                    arr[i] = eval(arr[i])
                x.append(arr[:3])
            else:
                print("===============in the end===============")
                break
    return np.array(x), y

def normalize(data):
    # 归一化
    min = np.min(data, axis=0)
    max = np.max(data, axis=0)
    diff = max - min
    result = (data - min) / diff
    return result, min, diff

def distance(data, point):
    # 计算欧式距离
    dis = (np.sum((data - point) ** 2, axis=1)) ** 0.5
    return dis

def classify(data, data_label, point, k):
    # 计算点属于什么类别
    dis = distance(data, point)
    sortIndex = np.argsort(dis)
    count = [0,0,0]
    for i in range(k):
        if data_label[sortIndex[i]] == 1:
            count[0] += 1
        elif data_label[sortIndex[i]] == 2:
            count[1] += 1
        elif data_label[sortIndex[i]] == 3:
            count[2] += 1
    return (count.index(max(count))+1)

if __name__ == '__main__':
    x, y = readFile("datingTestSet.txt")
    # x做标准化
    x_nomalize, min, diff = normalize(x)
    # 数据集划分
    x_train, x_test, y_train, y_test = train_test_split(x_nomalize, y, test_size=0.05)
    k = 5  # k个最近点
    y_pred = []  # 存放测试集的预测值
    for i in x_test:
        # 求测试集类别
        classify(x_train, y_train, i, k)
        y_pred.append(classify(x_train,y_train, i, k))
    print("y_test:\n", y_test)
    print("y_test的预测:\n", y_pred)
    #y_test长度
    ytextCount = len(y_test)
    #正确个数
    rightCount = Counter(np.array(y_test) == np.array(y_pred)).get(True)
    print("正确率:\n",rightCount/ytextCount)

    #随机生成点
    point = np.random.randint(1,11,3)
    print("随机点:\n",point)
    point_pred = classify(x_train, y_train, (point-min)/diff, k)
    print("随机点的类别:\n",point_pred)
